﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace team33_ass3
{
    class AStar
    {
        private Map map;

        public AStar(Map map)
        {
            this.map = map;
        }

        public List<MapBlock> getPath(Vector3 start, Vector3 dest)
        {
            MapBlock startBlock = map.getBlock(start);
            MapBlock destBlock = map.getBlock(dest);

            List<MapBlock> path = new List<MapBlock>();

            List<AStarNode> openList = new List<AStarNode>();
            List<AStarNode> closedList = new List<AStarNode>();
            
            // The search

            // Add the starting point into the OpenList
            AStarNode startNode = new AStarNode(startBlock, null, 0, 0);
            openList.Add(startNode);

            AStarNode currentNode = new AStarNode();
            AStarNode destinationNode = new AStarNode();

            while (openList.Count > 0)
            {
                //** Look for the lowest F cost square on the open list. We refer to this as the current block.
                //** Switch it to the closed list
                openList.Sort();
                currentNode = openList.First();
                openList.Remove(currentNode);
                closedList.Add(currentNode);

                //** All this stops when our destBlock is in the closed list.
                if (currentNode == destBlock)
                {
                    destinationNode = currentNode;
                    break;
                }


                //**Look at all the reachable or walkable blocks adajcent to this point (ignoring illegal terrain)
                List<MapBlock> adajcentBlocks = map.getWalkablesFrom(currentNode.block);

                //**For each of the 8 blocks to this current block
                foreach (MapBlock block in adajcentBlocks)
                {
                    AStarNode newNode = new AStarNode(block);

                    newNode.H = estimateScore(newNode.block, destBlock);
                    int currentScore = currentNode.G + newNode.G;

                    //**if its not walkable or if it is on the closed list, ignore it 
                    if (closedList.Contains(newNode))
                        continue;

                    //*if it is on the open list already, check to see if this path to that block is better, using G cost as the measure.
                    if (openList.Contains(newNode))
                    {
                        //*if it is better, change the parent of the block to the current block
                        //*Also if we're keeping the open list sorted by F score, we have to resort it.
                        if (newNode.G <= currentScore)
                            continue;

                    }
                    //**if it isn't on the open list, add it to the open list. make the current block the parent of this block

                    newNode.parent = currentNode;
                    newNode.G = currentScore;
                    newNode.F = currentScore + newNode.H;
                    openList.Add(newNode);

                }

                closedList.Add(currentNode);

                // EXTRA INFORMATION
                // Add them to the open list as well. Each of these squares should save A as its parent point
                // Drop the starting point from the list, and add it to the closed list that i dont have to look at again for now

                // We need to choose one of the adjacent squares on the open list.
                // To score its F = G + H
                // G = the movement cost to move from the starting point A to a given square on the grid, following the path generated to get there
                // H = the estimated movement cost to move from the given square on the grid to the final destination.

                // Drop the lowest score block from the open list and add it to the closed list
                //  check all of the adjacent squares and add them to the open list if they aren't on it
                //  make sure we make each of these squares have this block as their parent
                //  if they are already on the list
                //      we use our G score as reference.
                //      we look at them. and add out G score to theirs.
            }

            // Get all the MapBlocks out and filter out the list.
            AStarNode node = destinationNode;
            while (node.block != startNode.block)
            {

                path.Add(node.block);

                foreach (AStarNode n in closedList)
                {
                    if (node.parent == n)
                    {
                        node = n;
                        break;
                    }
                }
            }

            path.Reverse();

            return path;
        }

        static public int estimateScore(MapBlock block, MapBlock destination)
        {
            return Math.Abs(block.getX() - destination.getX()) + Math.Abs(block.getY() - destination.getY());
        }
    }
}
